package net.luerything.restlet.gwt.rpc;


import com.google.gwt.user.server.rpc.RPC;
import com.google.gwt.user.server.rpc.RPCRequest;
import com.google.gwt.user.server.rpc.SerializationPolicyProvider;
import org.restlet.data.CharacterSet;
import org.restlet.data.Disposition;
import org.restlet.data.MediaType;
import org.restlet.data.Status;
import org.restlet.representation.Representation;
import org.restlet.representation.StringRepresentation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static net.luerything.restlet.gwt.rpc.Commons.Result;

/**
 * Class handles GwtRpcImpl remote call for Restlet.
 *
 * @author keke
 */
public class GwtRpcImpl implements GwtRpc {

  private static final Logger LOG = LoggerFactory.getLogger(GwtRpcImpl.class);
  private SerializationPolicyProvider policyProvider;

  public GwtRpcImpl(final SerializationPolicyProvider policyProvider) {
    this.policyProvider = policyProvider;
  }

  @Override
  public Result doRpc(final Representation rep) {
    assert rep != null;
    return doRpc(this, rep);
  }

  @Override
  public Result doRpc(final Object target, final Representation req) {
    assert target != null;
    require(req.getMediaType().equals(Commons.GWT_RPC));
    require(req.getCharacterSet().equals(CharacterSet.UTF_8));
    try {
      final RPCRequest rpcRequest = RPC.decodeRequest(req.getText(), target.getClass(), policyProvider);
      assert rpcRequest != null;
      final String rpcResponse = RPC.invokeAndEncodeResponse(target, rpcRequest.getMethod(), rpcRequest.getParameters(),
          rpcRequest.getSerializationPolicy(), rpcRequest.getFlags());
      assert rpcResponse != null;
      final Representation result = new StringRepresentation(rpcResponse, MediaType.APPLICATION_JSON);
      result.setCharacterSet(CharacterSet.UTF_8);
      result.setDisposition(new Disposition(Disposition.TYPE_ATTACHMENT));
      return new Result(result, Status.SUCCESS_OK);
    } catch (Exception e) {
      LOG.error("Unable to invoke RPC call", e);
      final Representation result = new StringRepresentation("The call failed on the server; see server log for details", MediaType.TEXT_PLAIN);
      result.setCharacterSet(CharacterSet.UTF_8);
      return new Result(result, Status.SERVER_ERROR_INTERNAL);
    }
  }

  private static void require(boolean test) throws IllegalArgumentException {
    require(test, "Required condition failed");
  }

  private static void require(boolean test, String reason) throws IllegalArgumentException {
    if (!test) throw new IllegalArgumentException(reason);
  }
}
